package bll

import (
	"fmt"
	"io"
	"manager/utils/goroutine_pool"
	"signal/lib/trace_log"
	"time"

	"github.com/panjf2000/gnet/v2"
	log "signal/collector/logger"
	"signal/errors"
	"signal/model"
	"signal/utils"
)

var Handler = &handler{}

// 处理TCP连接的handler，实现了gnet.EventHandler接口
type handler struct {
}

func (h *handler) init() func() {
	return func() {}
}

func (h *handler) OnBoot(e gnet.Engine) (action gnet.Action) {
	log.Debug("server start")
	return
}

func (h *handler) OnShutdown(e gnet.Engine) {
	log.Debugf("server shutdown")
}

func (h *handler) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
	trace_log.CommonLogger.Info("connId:", c.Fd(), " OnOpen 建立了一个新连接")
	Session.OnOpen(c)
	return nil, gnet.None
}

func (h *handler) OnClose(c gnet.Conn, err error) (action gnet.Action) {
	trace_log.CommonLogger.Info("connId:", c.Fd(), " OnClose 1 关闭了一个新连接")
	Session.OnClose(c, err)
	return gnet.None
}

func (h *handler) OnTraffic(c gnet.Conn) (action gnet.Action) {
	var (
		n       int
		ip      = c.RemoteAddr().String()
		err     error
		buf     = make([]byte, 255)
		packets []*model.Packet
	)

	// read data from socket
	n, err = c.Read(buf)
	if err != nil {
		if err == io.EOF {
			return gnet.None
		}
		log.Errorf("read data from socket error: %s", err)
		return gnet.None
	}
	log.Infof("【Receive】 data from  device: %X", buf[:n])

	// parse protocol
	if packets, err = h.parse(buf[:n]); err != nil {
		log.Errorf("data is illegal, remote addr: %s", ip)
		return gnet.None
	}

	type Data struct {
		Packets []*model.Packet
		C       gnet.Conn
		Ip      string
	}

	// handle data
	goroutine_pool.GetPool().Push(&Data{
		Packets: packets,
		C:       c,
		Ip:      ip,
	}, func(data interface{}) error {
		d := data.(*Data)
		if d == nil {
			return nil
		}
		// do login
		if !Session.IsLogged(d.C.Fd()) {
			var resp []byte
			if len(packets) > 0 {
				if model.CmdType(d.Packets[0].Cmd) != model.CmdLogin {
					log.Error("device is not login, but the first packet is not login packet")
					c.Close()
					return nil
				}

				if resp, err = Session.Login(d.C, d.Packets[0].Body); err != nil {
					log.Error("login error: ", err)
					c.Close()
					return nil
				}

				if _, err = d.C.Write(resp); err != nil {
					log.Errorf("write to socket error: %s", err)
					return nil
				}
				if err = c.Flush(); err != nil {
					log.Errorf("write to socket error: %s", err)
					return nil
				}

				log.Info("device login success", ip)
				return nil
			}
		}

		sn := Session.GetSnByConnId(d.C.Fd())
		remoteAddr := fmt.Sprintf("%s://%s", c.RemoteAddr().Network(), c.RemoteAddr().String())
		log.Infof("【Receive】 data from  sn: %s device: %X", sn, buf[:n])
		trace_log.DebugLogger(sn, "OnTraffic 消息上行 receive:", fmt.Sprintf("%X", buf[:n]), " len:", n, " connId:", c.Fd(), "sn:", sn, "from:", remoteAddr)
		Session.UpdateKeepalive(d.C.Fd(), time.Now().Unix()+model.DefaultTCPTimeout)
		// parse command and route to handler
		for _, p := range d.Packets {
			if p == nil {
				continue
			}
			switch p.Magic {
			// handle device request
			case 0xfe:
				// handle query response
				if p.ID != 0 {
					h.replyHandle(d.C.Fd(), p)
					continue
				}
				// handle device report
				if err = h.reportHandle(d.C.Fd(), p); err != nil {
					log.Errorf("cmdHandle command error: %s", err)
				}
			// handle command response
			case 0xfd:
				h.replyHandle(d.C.Fd(), p)
			}
		}
		return nil
	})
	return gnet.None
}

func (h *handler) OnTick() (delay time.Duration, action gnet.Action) {
	return time.Second, gnet.None
}

func (h *handler) reportHandle(connId int, p *model.Packet) error {
	var (
		err error
		sn  = Session.GetSnByConnId(connId)
	)
	if sn == "" {
		log.Errorf("session not found, remote connId: %v", connId)
		return errors.New("session not found")
	}
	trace_log.DebugLogger(sn, fmt.Sprintf("reported %2X", p.Cmd))
	log.Debugf("sn:%s reported %2X", sn, p.Cmd)
	switch p.Cmd {
	case model.CmdHeartBeat:
		// 心跳
		//err = Report.HeartBeat(sn, p.Body)
		// 回复相同的心跳
		h.replyHeartBeat(connId, p)
	case model.CmdFault:
		// 故障上报
		err = Report.Fault(sn, p.Body)
	case model.CmdAlarm:
		// 告警上报
		err = Report.Alarm(sn, p.Body)
	case model.CmdUpgrade:
		// 升级申请
		err = Report.OTAUpgrade(sn, p.Body)
	case model.CmdInfo0x81:
		err = Report.Location(sn, p.Body)
	case model.CmdInfo0x82:
		err = Report.ReportEveryMinute(sn, p.Body)
	case model.CmdInfo0x8A:
		err = Report.FullReport(sn, p.Body)
	}
	return err
}

// 应答处理
func (h *handler) replyHandle(connId int, requestPkt *model.Packet) {
	if sn := Session.GetSnByConnId(connId); sn != "" {
		requestKey := RequestKey(sn, model.CmdType(requestPkt.Cmd), requestPkt.ID)
		Session.Reply(requestKey, requestPkt)
		return
	}
	log.Errorf("session not found, remote connId: %v", connId)
}

// 应答心跳包
func (h *handler) replyHeartBeat(connId int, requestPkt *model.Packet) {
	if sn := Session.GetSnByConnId(connId); sn != "" {
		requestKey := RequestKey(sn, model.CmdType(requestPkt.Cmd), requestPkt.ID)
		if err := Session.ReplyHeartBeat(sn, requestKey, requestPkt); err == nil {
			log.Debugf("sn:%s, reply heart beat succeed", sn)
		} else {
			log.Errorf("sn:%s, reply heart beat failed:%+v", sn, err)
		}
		return
	}
	log.Errorf("session not found, remote connId: %v", connId)
}

func (h *handler) parse(data []byte) ([]*model.Packet, error) {
	var (
		err     error
		packets = make([]*model.Packet, 0)
		reader  = utils.NewBitReader(data)
	)

	for {
		p := &model.Packet{}
		readable, _ := reader.AvailBits()
		if readable == 0 {
			break
		}

		if p.Magic, err = reader.ReadBits8(8); err != nil {
			log.Errorf("read Magic error: %s", err)
			return packets, err
		}

		if p.Magic == 0xfe || p.Magic == 0xfd {
			if p.Len, err = reader.ReadBits8(8); err != nil {
				log.Errorf("read Len error: %s", err)
				return packets, err
			}
			if p.ID, err = reader.ReadBits8(8); err != nil {
				log.Errorf("read ID error: %s", err)
				return packets, err
			}
			if p.Cmd, err = reader.ReadBits8(8); err != nil {
				log.Errorf("read Cmd error: %s", err)
				return packets, err
			}
			if p.Body, err = reader.ReadBytes(uint(p.Len - 2)); err != nil {
				log.Errorf("read Body error: %s", err)
				return packets, err
			}
			packets = append(packets, p)
		} else {
			break
		}
	}
	return packets, nil
}
